/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package levelSystem;

import java.awt.*;
import java.awt.event.KeyEvent;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;

import tools.Constants;
import tools.Game;

public abstract class GameLevel {
    /* Classe basica de levels 
     * Representa um level, que e um mapa, ou um estado
     * como um menu, por exemplo.
     * Todo o jogo roda em torno do level. */
    
	/* Save */
	Game game;
	
	/* Handlers */
	protected LevelHandler levelHandler;
	protected KeyProcessor keyHandler;
	
    /* Variaveis gerais */ 
	protected boolean debugOn;
	
	protected int alpha;
	protected int alphaFrame;
	protected int maxAlphaFrame;
	protected int fadeType;
	protected boolean fadeFinished;
	
	/* Variaveis de tempo */
	protected volatile boolean paused;
	protected volatile boolean canPause;
	protected volatile boolean gameOver;
	protected volatile boolean canChangeLevel;
	protected volatile boolean retry;
	
	/* Som */
	protected Thread songThread;
	protected Sequencer player;
	protected Sequence song;
	protected Runnable songRun;
	
	/* Construtor */
	public GameLevel() {
		game = null;
		
		retry = false;
		
		levelHandler = null;
		keyHandler = new KeyProcessor(this){};
		
		debugOn = false;	
		
		paused = false;
		canPause = true;
		gameOver = false;
		
		alpha = 255;
		alphaFrame = 0;
		maxAlphaFrame = 2;
		fadeType = 1;
		fadeFinished = false;
		
		canChangeLevel = false;
		
		songThread = null;
		player = null;
		song = null;
		
		songRun = new Runnable() {
			
			public void run() {
				if(song == null || player == null) levelHandler.quit();
		        else
		        {
		        	try
		        	{
			        	player.setSequence(song);
			        	player.open();
			        	player.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
			        	player.setTempoFactor(1.0f);
			        	player.start();
		        	}catch(InvalidMidiDataException e)
		        	{
		        		e.printStackTrace();
		        	}catch(MidiUnavailableException e)
		        	{
		        		e.printStackTrace();
		        	}
		        }
			}
		};
	}
	
    /* Basico do game loop (event>logic>[render>draw]) */
    public void processKey(KeyEvent e, int type)
    {
        keyHandler.processKey(e, type);
    }
    public abstract boolean logic();
    public abstract void renderLevel(Graphics g); // render feito pela janela principal
    public void fadeIn(Graphics g, int w, int h)
    {
    	alphaFrame++;
    	if(alphaFrame >= maxAlphaFrame)
    	{
    		alphaFrame = 0;
    		alpha -= Constants.FADE_AMOUNT;
    		if(alpha < 0)
    		{
    			alpha = 0;
    			fadeFinished = true;
    		}
    	}
    	g.setColor(new Color(0, 0, 0, alpha));
    	g.fillRect(0, 0, w, h);
    }
    public void fadeOut(Graphics g, int w, int h)
    {
    	alphaFrame++;
    	if(alphaFrame >= maxAlphaFrame)
    	{
    		alphaFrame = 0;
    		alpha += Constants.FADE_AMOUNT;
    		if(alpha > 255) 
    		{
    			alpha = 255;
    			fadeFinished = true;
    		}
    	}
    	g.setColor(new Color(0, 0, 0, alpha));
    	g.fillRect(0, 0, w, h);
    }
    
    public void setFadeOutComponents()
    {
    	if(fadeFinished || fadeType != 2)
    	{
	    	fadeFinished = false;
	    	alpha = 0;
	    	fadeType = 2;
    	}
    }
    public void activateLevelChange()
    {
    	canChangeLevel = true;
    }
    
    /* Processamento de estados */
    public void pauseGame()
    {
    	paused = true;
    }
    public void resumeGame()
    {
    	paused = false;
    }
    public boolean isPaused()
    {
    	return paused;
    }
    public boolean canPause()
    {
    	return canPause;
    }
    
    /* Essentials */
    public abstract void init();
    public abstract void init(int x, int y);
    
    public void setLevelHandler(LevelHandler lh)
    {
    	levelHandler = lh;
    }
    public void changeLevel(GameLevel g)
    {
    	levelHandler.changeLevel(g);
    }
    
    public void debugSwitch()
    {
    	debugOn = !debugOn;
    }
    
    /* Processamento de setas */
    protected abstract void procUp(int i);
    protected abstract void procDown(int i);
    protected abstract void procLeft(int i);
    protected abstract void procRight(int i);
    
    /* processamento do ASDW enter */
    protected abstract void procA(int i);
    protected abstract void procS(int i);
    protected abstract void procD(int i);
    protected abstract void procW(int i);
    protected abstract void procEnter(int i);
    
    /* Debug */
    protected abstract void showDebug(Graphics g);

    public boolean isChangingLevel()
    {
    	return canChangeLevel;
    }
    public boolean isDebugOn()
    {
    	return debugOn;
    }
    public boolean isGameOver()
    {
    	return gameOver;
    }

	public void retry() {
		retry = true;
	}

	public boolean returned() {
		return canChangeLevel;
	}
	public boolean retryed()
	{
		return retry;
	}
}
